package com.khaled.protclass.model.feature.blast;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Random;

import org.apache.log4j.Logger;

import com.khaled.protclass.model.base.BaseFeature;
import com.khaled.protclass.model.base.IProtein;
import com.khaled.protclass.model.bean.domain.Domain;
import com.khaled.protclass.model.bean.domain.FeatureRepresentation;
import com.khaled.protclass.model.bean.weka.Attribute;
import com.khaled.protclass.model.bean.weka.WekaDataTypes;
import com.khaled.protclass.model.util.UtilDirectory;

public class CD extends BaseFeature
{
	/** Logger. */
	protected static Logger logger = Logger.getLogger(CD.class);

	/** Feature Name. */
	private static final String NAME = "CD";

	public static final String BLAST_PATH = "NCBI-PATH";

	public static final String DB_PATH = "DB-PATH";
	
	public static final String FEATURE_CD_CUT_OFF = "cut-off";

	public static final String FEATURE_CD_REPRESENTATION = "representation";

	public static final String FEATURE_CD_ALL_DOMAINS = "all-domains";

	public static final String FEATURE_CD_POSSIBLE_DOMAINS = "possibleDomains";


	/** Hit Threshold. */
	protected static double HIT_THRESHOLD = 0.001;

	/** Domain identifier symbol in the output file. */
	private static final String DOMAIN_IDENTIFIER = "gnl|CDD|";

	/** All characteristic domains in CDD version 3.09. */
	protected ArrayList<String> CDD_DOMAINS = new ArrayList<String>();

	private FeatureRepresentation fRep = null;

	private Hashtable<String, String> domainsLength = null;

	@Override
	public void init()
	{
		try
		{
			String possibleDomainsFilePath = getProperties().get(FEATURE_CD_POSSIBLE_DOMAINS);

			ArrayList<String> cdLines = UtilDirectory.readFileLines(possibleDomainsFilePath);
			for (String line : cdLines)
			{
				String strLine = line.trim();
				String domainId = strLine;

				int tabIndex = strLine.indexOf("	");
				if (tabIndex > 0)
				{
					domainId = strLine.substring(0, tabIndex);
				}

				CDD_DOMAINS.add(domainId);
			}

			// ///////////////////Retreive HIT Cutoff////////////////////
			HIT_THRESHOLD = Double.parseDouble(getProperties().get(FEATURE_CD_CUT_OFF));

			// ///////////////////Retreive Feature
			// Representation////////////////////
			this.fRep = new FeatureRepresentation(getProperties().get(FEATURE_CD_REPRESENTATION));

			// ///////////////////Retreive domains lengthes ////////////////////
			this.domainsLength = new Hashtable<String, String>();
			String allDomainsPath = getProperties().get(FEATURE_CD_ALL_DOMAINS);
			
			if(allDomainsPath != null)
			{
				ArrayList<String> allDomainsLines = UtilDirectory.readFileLines(allDomainsPath);
				for (String line : allDomainsLines)
				{
					String strLine = line.trim();
					String[] items = strLine.split("	");
					domainsLength.put(items[0], items[4]);
				}
			}
			// esle ignore it
			
			
		}
		catch (IOException e)
		{
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		catch (Exception e)
		{
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}

	@Override
	public String getName()
	{
		return NAME;
	}

	@Override
	public ArrayList<Attribute> getAttributes()
	{
		ArrayList<Attribute> attributes = new ArrayList<Attribute>();

		for (String d : CDD_DOMAINS)
		{
			attributes.add(new Attribute("D_" + d, WekaDataTypes.NUMERIC));

		}

		return attributes;
	}

	@Override
	public String getFeatureVector(IProtein protein)
	{
		logger.info("start getFeatureVector for protein ID: " + protein.getProteinID());
		StringBuffer featureVector = new StringBuffer();

		ArrayList<Domain> domains = getConservedDomains(protein);

		for (String dID : CDD_DOMAINS)
		{
			Domain domainHIT = null;

			// ////////////////////////////////
			for (Domain d : domains)
			{
				if (dID.equals(d.getDomain_ID()))
				{
					domainHIT = d;
					break;
				}
			}
			// //////////////////////////////

			if (domainHIT != null)
			{
				if (HIT_THRESHOLD == -1)
				{
					featureVector.append(getValue(domainHIT, true)).append(",");
				}
				else
				{
					if (domainHIT.getE_Value() <= HIT_THRESHOLD)
					{
						featureVector.append(getValue(domainHIT, true)).append(",");
					}
					else
					{
						featureVector.append(getValue(domainHIT, false)).append(",");
					}
				}
			}
			else
			{
				featureVector.append(getValue(domainHIT, false)).append(",");
			}
		}
		return featureVector.toString();
	}

	private String getValue(Domain domainHIT, boolean hit)
	{
		if (fRep.isBinary())
		{
			if (hit) { return "1"; }
		}
		else if (fRep.isBitscore())
		{
			if (hit) { return Double.toString(domainHIT.getBitScore()); }
		}
		else if (fRep.iseValue())
		{
			if (hit) { return Double.toString(domainHIT.getE_Value()); }
		}
		else if (fRep.isDomainLength())
		{
			if (hit) { return domainHIT.getLength(); }
		}

		return "0";
	}

	protected ArrayList<Domain> getConservedDomains(IProtein protein)
	{
		ArrayList<Domain> domains = new ArrayList<Domain>();
		String cdFilePath = null;
		try
		{
			cdFilePath = getCDFilePath(protein);

			ArrayList<String> cdFileLines = UtilDirectory.readFileLines(cdFilePath);

			for (int i = 0; i < cdFileLines.size(); i++)
			{
				String line = cdFileLines.get(i).trim();

				if (line.startsWith(DOMAIN_IDENTIFIER))
				{
					String domainID = line.substring(0, line.indexOf(" "));
					domainID = domainID.substring(domainID.lastIndexOf("|") + 1);

					String eValue = line.substring(line.lastIndexOf(" "));

					String line2 = line.substring(0, line.lastIndexOf(" ")).trim();
					String bitScore = line2.substring(line2.lastIndexOf("  "));

					Domain d = new Domain(domainID, Double.parseDouble(eValue), Double.parseDouble(bitScore));
					// d.setLength(getDomainLength(domainID, cdFileLines));
					if (domainsLength != null) d.setLength(domainsLength.get(domainID));

					domains.add(d);

				}

				/*
				 * Hack: for performance improvement, as after listing all
				 * domains, the symbol > will be appear in the file and in this
				 * case i don't need the remaining of the file
				 */
				else if (line.toString().startsWith(">"))
				{
					break; // for performance issue.
				}

			}
		}

		catch (NumberFormatException e)
		{
			logger.error("Problem while trying to load blast file for protein ( " + protein.getProteinID() + ").");
			logger.error("Error in formatting the e-Value number:");
			logger.error(e.getMessage());
			e.printStackTrace();
		}

		catch (Exception e)
		{
			logger.error("Problem while trying to load blast file for protein ( " + protein.getProteinID() + ").");
			logger.error("Error while loading file:" + cdFilePath);
			logger.error(e.getMessage());
			e.printStackTrace();
		}

		return domains;
	}

	public String getCDFilePath(IProtein protein) throws Exception
	{
		// 1. Generate file names
		Random r = new Random();
		String protID = "protein" + r.nextInt(1000);
		String inputFile = getProperties().get(BLAST_PATH) + "\\" + protID + ".fasta";
		String cdFilePath = getProperties().get(BLAST_PATH) + "\\" + protID + ".blast";

		// 2. Create the input fasta file in the workingDicrectory.
		UtilDirectory.createFile(protein.getProteinSequence(), inputFile);

		// 3. Prepare the rpsblast command.
		String command = rpsblast(getProperties().get(DB_PATH), inputFile, cdFilePath);
		// 4. Run the command.
		File workDir = new File(getProperties().get(BLAST_PATH));
		Runtime systemShell = Runtime.getRuntime();
		Process shellOutput;
		try
		{
			shellOutput = systemShell.exec(workDir + "\\" + command, null, workDir);

			InputStreamReader isr = new InputStreamReader(shellOutput.getInputStream());
			BufferedReader br = new BufferedReader(isr);

			// Waiting
			while (br.readLine() != null)
			{
				// DO NOTHING
			}
			shellOutput.destroy();
			// 5. remove fasta file
			UtilDirectory.deleteFile(inputFile);
		}
		catch (IOException e)
		{
			logger.error("Error while generating the CD for protein: " + protein.getProteinSequence());
			logger.error(e);
			// 5. remove fasta file
			UtilDirectory.deleteFile(inputFile);
			throw new Exception(e.getMessage());
		}

		return cdFilePath;
	}

	private String rpsblast(String dbPath, String inputFilePath, String outputFilePath)
	{
		StringBuffer command = new StringBuffer("");

		command.append("rpsblast ");
		command.append("-query ").append(" \"").append(inputFilePath).append("\" ");
		command.append("-db \"").append(dbPath).append("\\Cdd\" ");
		command.append("-out \"").append(outputFilePath).append("\"");

		return command.toString();
	}

}
